import isEmpty from 'lodash/isEmpty'
import spanner from '../index'
import { isLocalhost } from './EnvUtils'
import { GJCFG_ID, TROPE_KV, QUEST_IS, SLASH_IS,
  POUND_RP, QUERY_RP, SLASH_RP, VISIT_IS, MATCH_KV,
  MATCH_VER, SLICE_URL, PARSE_URL, PARSE_ANCHOR, PARSE_SERVER
} from './const'

const _qry2obj = (qry: string, decode: boolean): object => {
  const obj = {}
  if (qry.length > 0) {
    let arr
    do {
      // => ["a=1", "a", "1", index: 1, input: "?a=1&b=2", groups: undefined]
      if (!(arr = MATCH_KV.exec(qry))) {
        break
      }
      const [, key, val] = arr
      if (key.length > 0) {
        obj[key] = decode ? decodeURIComponent(val) : val
      }
    } while (true)
  }
  return obj
}

const _obj2qry = (obj: object, encode?: boolean): string => {
  const qry: string[] = []
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      qry.push(`${key}=${encode ? encodeURIComponent(obj[key]) : obj[key]}`)
    }
  }
  return qry.join('&')
}

const _mergeQuery = (newQuery: object, oldQuery: string, encode?: boolean): string => {
  const query = _obj2qry({
    ..._qry2obj(oldQuery, true),
    ...newQuery
  }, encode)
  return query.length === 0 ? oldQuery : `?${query.replace(QUERY_RP, (i) => TROPE_KV[i] || i)}`
}

/**
 * 拼装url
 *   - 调用方式：UrlUtils.build(path, query, anchor)[.withMac()].create(true|false)
 * @param path
 * @param query
 * @param anchor
 * @returns {UrlBuild}
 */
export const build = (path: string, query: object, anchor: string): UrlBuild => {
  return new UrlBuild(path, query, anchor)
}

interface Parsed {
  getProtocol: () => string
  getHost: () => string
  getPort: () => string
  getPath: () => string
  getHash: () => object
  getQuery: () => string
}
export const parse = (url = window?.location?.href): Parsed => {
  // 相关参考：
  // https://stackoverflow.com/a/26766402/10352440
  // https://tools.ietf.org/html/rfc3986#appendix-B
  const [, _prot = '', server = '', _path = '', query = '', anchor = ''] = (
    url.length > 0 ? url.match(PARSE_URL) || [] : []
  )
  const [, _hash = '', param = ''] = (
    anchor.length > 0 ? anchor.match(PARSE_ANCHOR) || [] : []
  )
  const [, _host = '', _port = ''] = (
    server.length > 0 ? server.match(PARSE_SERVER) || [] : []
  )
  return ((prot: string, host: string, port: string, path: string,
      hash: string, pathQuery: object, hashParam: object) => {
    return {
      getProtocol() {
        return prot
      },
      getHost() {
        return host
      },
      getPort() {
        return port
      },
      getPath() {
        return path
      },
      getHash() {
        return {
          route: hash,
          param: hashParam
        }
      },
      getQuery(key?: string) {
        return key === undefined ? pathQuery : (
          key.length > 0 ? pathQuery[key] || '' : ''
        )
      }
    }
  })(_prot, _host, _port, _path, _hash, _qry2obj(query, true), _qry2obj(param, true))
}

/**
 * 拼接hash部分（Param中，新值会覆盖旧值）
 * @param hash '[#]<hash>'
 * @param param
 * @param encode
 * @returns {string} '#[<hash>][?<query>]'
 */
export const diyAnchor = (hash: string, param?: any, encode?: boolean): string => {
  if (hash.length > 0) {
    hash = hash.replace(POUND_RP, '')
    if (!isEmpty(param)) {
      const ask = hash.indexOf('?')
      if (ask === -1) {
        hash += _obj2qry(param, encode)
      } else {
        hash += _mergeQuery(param, hash.slice(ask), encode)
      }
    }
    return `#${hash}`
  }
  return ''
}

/**
 * 判断当前组件是否被统一域名方式接入
 * @param compUrlKey 组件url在Gaea配置中的key
 * @param gaeaConf Gaea配置
 * @param pageHref 页面地址
 * @returns {boolean}
 */
export const isUnifiedSite = (compUrlKey: string, gaeaConf: string = window?.[GJCFG_ID], pageHref: string): boolean => {
  if (!isLocalhost()) {
    const conf = gaeaConf?.[compUrlKey]
    if (conf) {
      const href = parse(pageHref)
      return `${href.getProtocol()}//${href.getHost()}` !== conf
    }
  }
  return false
}

/**
 * 接入外部组件时，获取合适的地址：
 *   - 如果原组件是统一域名，则返回外部组件的统一域名形式。
 *   - 如果原组件是组件域名，则返回外部组件的组件域名形式。
 * @param originUrlKey 原组件url在Gaea配置中的key
 * @param targetUrlKey 目标组件url在Gaea配置中的key
 * @param targetNameKey 目标组件名在Gaea配置中的key
 * @param gaeaConf Gaea配置
 * @param pageHref 页面地址
 * @returns {string}
 */
export const getTargetSite = ({
  originUrlKey,
  targetUrlKey,
  targetNameKey
}, gaeaConf: string = window?.[GJCFG_ID], pageHref: string): string => {
  let site = ''
  // 组件域名
  if (!isUnifiedSite(originUrlKey, gaeaConf, pageHref)) {
    site = gaeaConf?.[targetUrlKey] || ''
    if (site) {
      site = site.replace(SLASH_RP, '')
    }
  } else {
    // 统一域名
    const urlParse = parse(pageHref)
    if (urlParse) {
      site = `${urlParse.getProtocol()}//${urlParse.getHost()}`
      if (targetNameKey.length > 0) {
        const comp = gaeaConf?.[targetNameKey] || ''
        if (comp) {
          site += `/${comp}`
        }
      }
    }
  }
  return site
}

/**
 * 拼装目标地址
 * @param path 未加zuul代理的url，形式为：{host}/{api} 或 {host}/{component_name}/{api}
 * @param zuul 可选，zuul代理字符串，形式多为以下形式：api-{component_name}
 * @returns {string}
 */
export const useZuul = (path: string, zuul: string): string => {
  if (path.length > 0) {
    if (zuul.length > 0) {
      const ver = path.search(MATCH_VER)
      if (ver !== -1) {
        path = `${path.slice(0, ver + 1)}/${zuul}${path.slice(ver + 1)}`
      }
    }
    return path
  }
  return ''
}
/**
 * 获取组件Host
 * @param compUrlKey 组件Url在Gaea配置中的key
 * @param gaeaConf Gaea配置
 * @param pageHref 页面地址
 * @returns {string}
 */
export const getEduHost = (compUrlKey: string, gaeaConf: string = window?.[GJCFG_ID], pageHref: string): string => {
  const config = compUrlKey.length > 0 ? gaeaConf?.[compUrlKey] || '' : ''
  if (!VISIT_IS.test(config)) {
    const urlParse = parse(pageHref)
    return `${urlParse.getProtocol()}//${urlParse.getHost()}${
      config.length > 0 ? (!SLASH_IS.test(config) ? `/${config}` : config) : ''
    }`
  }
  return config
}

// URL构建器
class UrlBuild {
  private __mac: boolean
  private _path: string
  private _query: object
  private _anchor: string
  constructor(path, query, anchor) {
    this.__mac = false
    this._path = path
    this._query = query
    this._anchor = anchor
  }

  withMac = (): UrlBuild => {
    this.__mac = true
    return this
  }

  create = (encode = true): string => {
    const [, urlPrefix = '', oldQuery = '', oldAnchor = ''] = (
      this._path.length > 0 ? this._path.match(SLICE_URL) || [] : []
    )
    // 拼接URL
    const urlQuery = _mergeQuery(
      this._query, oldQuery, encode
    )
    const urlAnchor = diyAnchor(this._anchor) || oldAnchor
    const urlResult = `${urlPrefix}${urlQuery}${urlAnchor}`
    if (this.__mac && spanner.uc) {
      return `${urlResult}${QUEST_IS.test(urlAnchor || urlQuery || urlPrefix) ? '&' : '?'}__mac=${spanner.uc.generateMac(urlResult)}`
    }
    return urlResult
  }
}
